package lock

import (
	"context"
	"github.com/redis/go-redis/v9"
	"time"
)

// 非公平锁

type NonFairSync struct {
	lock   *Lock
	unCh   chan int32
	key    string
	expire int64
}

//NewNonFairSync  初始化一个非公平锁对象
//key 设置一个锁的键，同一个锁的体系里，需要使用相同的键名。
func NewNonFairSync(client *redis.Client, key string) SyncInterface {
	return &NonFairSync{
		lock:   NewLock(client, []string{key}),
		unCh:   make(chan int32, 0),
		key:    key,
		expire: Expire,
	}
}

// Lock 加锁
func (rdb *NonFairSync) Lock(ctx context.Context) error {
	rdb.expire = rdb.lock.Deadline(ctx)
	result, err := rdb.lock.Acquire(ctx, NonFairSyncLockScript, DefaultValue, rdb.expire*Second)
	if err != nil {
		return err
	}
	acquire := rdb.tarn(result)
	// 获取到锁
	if acquire == 0 {
		go rdb.cas(ctx)
		return nil
	}
	ttl := rdb.lock.TTL(ctx)
	select {
	case <-ctx.Done():
		// 超时退出
		return nil
	case <-time.After(ttl): // 到点重新抢占
		return rdb.Lock(ctx)
	}
}

// UnLock 解锁
func (rdb *NonFairSync) UnLock(ctx context.Context) error {
	// 断开自旋
	defer func() {
		rdb.unCh <- 1
	}()
	_, err := rdb.lock.Un(ctx, UnlockScript, DefaultValue)
	if err != nil {
		return err
	}
	return nil
}

// 自旋
func (rdb *NonFairSync) cas(ctx context.Context) {
	select {
	case <-rdb.unCh:
		return
	case <-ctx.Done():
		// 超时处理 直接释放锁
		_ = rdb.UnLock(context.TODO())
		return
	case <-time.After(time.Second * time.Duration(rdb.expire/2)):
		// 自旋增加失效的时间
		err := rdb.lock.Refresh(ctx, rdb.expire)
		if err != nil {
			// 失败直接断开连接
			return
		}
		rdb.cas(ctx)
	}
}

func (rdb *NonFairSync) tarn(value interface{}) int64 {
	switch value.(type) {
	case string:
		return 0
	case int64:
		return value.(int64)
	default:
		return 1
	}
}
